热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

函数|参数_Kotlin用函数编程

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Kotlin用函数编程相关的知识,希望对你有一定的参考价值。文章目录1

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Kotlin 用函数编程相关的知识,希望对你有一定的参考价值。



文章目录


  • 1. 函数的概念
    • 1.1 数学中的函数
      • 1.1.1 偏函数
      • 1.1.2 多参数的函数
      • 1.1.3 柯里化函数
      • 1.1.4 偏应用函数

    • 1.2 Kotlin中的函数
      • 1.2.1 纯函数
        • 1.2.1.1 定义
        • 1.2.1.2 例子

      • 1.2.2 值函数
      • 1.2.3 复合函数


  • 2. 高级函数的特征
    • 2.1 多参数函数
    • 2.2 高阶函数的定义
      • 2.2.1 compose 的多态高阶版本

    • 2.3 使用匿名函数
    • 2.4 闭包
    • 2.5 应用偏函数和自动柯里化
      • 2.5.1 例1
      • 2.5.2 例2
      • 2.5.3 例3

    • 2.6 切换偏应用函数的参数
    • 2.8 使用正确的类型
      • 2.8.1 避免使用标准类型带来的错误
      • 2.8.2 定义值类型


  • 总结

目标主要是为了弄清楚:
● 使用函数式风格来编程的意义是什么?
● 为什么我要将函数作为参数传递?我定义一个接口,让他们来调用不就好了?我为什么要把函数作为一个值


1. 函数的概念

1.1 数学中的函数

函数是我们从小到大就在数学中接触的概念,在数学课本中函数的定义是这样的:



给定一个数集A,假设其中的元素为x,对A中的元素x施加对应法则f,记作f(x),得到另一数集B,假设B中的元素为y,则y与x之间的等量关系可以用y=f(x)表示,函数概念含有三个要素:定义域A、值域B和对应法则f。其中核心是对应法则f,它是函数关系的本质特征。


也就是定义域到值域的映射关系。 例如 successor(x) = x + 1 的函数表达的正整数和输出结果的关系, 函数名称叫 successor
在数学中, 对函数起名字并不是一个必要的工作,只是为了方便使用它,这没有错,但是函数名称和函数定义是不存在强关系的, successor 也可以被其他名字替换, 它本身不代表什么。

假定 A 是定义域, B 是值域, 在 Kotlin 的语法中则可以表示为 : (A) -> B , 其反函数(求导) 则为 (B) -> A


1.1.1 偏函数

下面有两个条件


  • A. 必须是定义域对所有元素进行定义
  • B. 定义域中的元素不能和值域中的多个元素对应

满足 A & B 的函数称为 “全函数”, 而 !A & B 的称为 “偏函数”

严格意义上来说 , 偏函数不是函数,全函数才是真男人。

为什么我们要知道这个看起来很冷的词汇呢? 这是因为在编程中,许多错误就是开发者把偏函数当成全函数来使用。

例如 f(x) = 1/ x 是一个 N 到 Q 的偏函数, 因为 定义域没有对 0 进行进行定义, 所以当输入 x = 0 时,会输出错误,这是不符合函数的预期的。

f(x) = 1 / x , x属于N*f(x) = 1 / x , 值域为 Q 或错误值(N 到 Q或错误值) 则是全函数, 我们输入 x = 0 时,要么是正确结果,要么是符合预期的错误结果。

偏函数转化成全函数是安全编程的一个重要部分!!!!!, 而上面转化成全函数的两种做法也正是我们常用的处理偏函数的常用方式,即:


  1. 对定义域进行指定、定义
  2. 向值域添加错误的元素

1.1.2 多参数的函数

在编程中, 我们的函数经常看起来不止有一个入参,例如 f(x, y) = x * y,而函数的定义是 一个源集 到 一个目标集 的关系,那这还是函数吗?

答案是肯定的,我们引入了 元组(tuple) 这个特例概念, 即 (x, y) 甚至 (x, y, z) 都是一个元组,这个元组的定义域就是源集。所以是没有 多参数函数 这种概念的。


1.1.3 柯里化函数

柯里化函数是对上面所讲的 元组函数 的变形。
假定 f(x, y) = x + y ,那么有以下逻辑推演:

因为 f(x, y) = x + y 定义域为 N, 值域为 N
假定 g(x) = h 定义域 x 属于 N, 值域为 自变量x 的 映射函数h
假定 h(y) = x + y 定义域为 N, 值域为 N
因为f函数和g函数的定义域、值域都相同, 是 N 到 N,且最终结果表达式为 x + y
所以 f(x, y) = g(x)(y)
将 g 函数改名为 f
得出 f(x, y) = f(x)(y)

(上面的推演是我自己写的,数学好的大佬不要骂我,我就是这样蛊惑自己的哈哈哈哈啊哈哈哈哈哈)

f(x)(y) 就是 f(x, y) 的柯里化形式, 数学中称这种函数为柯里化函数


1.1.4 偏应用函数

这个概念很好理解,它是在柯里化函数上进行深化的。

例如函数: f(x, y) = x + y 那么它等价于 f(x)(y)
f(x) 是什么呢? 它代表的是自变量 x 对应的映射函数 , 当 x = 1时,f(1) 的结果 是一个函数,这个函数:输入是 y, 结果是 y 加上这个1。 那么我们称 f(x)f(x, y) 对x的偏应用函数 ,偏应用函数会对自变量计算产生很大的影响,这个我们会在后面讲到。


1.2 Kotlin中的函数

在 Kotlin 中:


  1. 函数是数据
    函数是有类型的,可以被传递,也可以被返回,也可以被放到集合中
  2. 数据也可以是函数
    数据可以看成是 源集任意,目标集只有一个 , 也可以称为 常函数
    例如 val x = 5 , 可以看成是一个 f(x)=5 的函数,就是自变量无关的一种特殊函数

1.2.1 纯函数


1.2.1.1 定义

前面讲过数学中的全函数, 在 Kotlin 中,程序员创造了一个与之相似的概念,叫 “纯函数”, 这是因为虽然编程语言定义了 fun 关键字来声明函数,但是很多时候,程序员所写的函数很少能称为真正的函数,所以提出这个概念,愿景是希望Kotlin开发者能够多写真正的函数。

函数要成为纯函数的条件如下:


  • 不能改变函数外界的任何事物
  • 内部的改变对外部不可见
  • 不能改变入参
  • 对于相同的参数, 无论何时执行,始终只返回同一个值
  • 函数不能抛出异常或错误(不能出现crash)
  • 始终返回一个值

1.2.1.2 例子

请看下面代码的 1~9 的方法, 想想哪些函数是纯函数:

class Sample
var percent1 = 5
private var percent2 = 9
val percent3 = 13
fun add(a: Int, b: Int): Int = a + b // 1
fun mult(a:Int, b: Int?): Int = 5 // 2
fun div(a: Int, b: Int): Int = a / b // 3
fun div(a: Double, b: Double): Double = a / b // 4
fun applyTax1(a: Int): Int = a / 100 * (100 + percent1) // 5
fun applyTax2(a: Int): Int = a / 100 * (100 + percent2) // 6
fun applyTax3(a: Int): Int = a / 100 * (100 + percent3) // 7
fun append1(i: Int, list: MutableList<Int>): List<Int>
list.add(i)
return list
// 8
fun append2(i: Int, list: List<Int>) &#61; list &#43; i // 9

第一个&#xff1a;纯函数
第二个&#xff1a;纯函数&#xff0c; 而且是常函数
第三个&#xff1a;不是纯函数&#xff0c; 因为当 b &#61;&#61; 0 时&#xff0c; 程序会抛出错误
第四个&#xff1a;是纯函数&#xff0c;因为当 b &#61;&#61; 0.0 时&#xff0c;会返回 Double.Infinity
第五个&#xff1a;不是纯函数&#xff0c; 因为 percent1 是公有的&#xff0c; 在两次调用该函数的期间&#xff0c; 这个 percent1 有可能会被外界改变&#xff0c;所以函数可能会返回不一样的值
第六个&#xff1a;是纯函数&#xff0c; 虽然依赖的 percent2 是可变的&#xff0c;但是该类中没有其他地方去改变这个值&#xff0c;而且因为它是私有的所以它不能被外界所改变。但是这种是不安全的&#xff0c;推荐将 percent2 改成 val 来声明
第七个&#xff1a;对于 参数 a 是纯函数&#xff0c;因为 percent3 是不可变的
第八个&#xff1a;非纯函数&#xff0c; 它改变了入参 list 的内容
第九个&#xff1a;纯函数&#xff0c;因为 list &#43; i 返回的是一个新的 List 对象


1.2.2 值函数

Kotlin 是允许将函数写成数据的
例如 下面的函数&#xff1a;

fun add(a: Int, b: Int): Int &#61; a &#43; b

等价于&#xff1a;

val sum :(Int, Int) -> Int &#61; a, b -> a &#43; b

这里用了 lambda 表达式&#xff0c;这里就不再赘述&#xff0c;之前学习过&#xff1a;Lambda学习

在 Kotlin 中&#xff0c;函数有两种定义形式&#xff0c;一种是通过 fun 关键字定义&#xff0c;一种是使用 值 来定义&#xff0c;他们的区别是什么呢&#xff1f;为什么不像 Java 那样只用一种方法来定义呢&#xff1f;


  1. 通过 fun
    Kotlin 是会优化 fun 关键字声明的函数&#xff0c;更优效率&#xff0c;并且更加美观
  2. 通过值函数
    可以作为数据传递&#xff0c; 或者作为变量存储到 list、map中
    &#xff08;当然 fun 声明的函数也可以做为对象传递&#xff0c; 使用 ::funName 的形式 &#xff09;

1.2.3 复合函数

下面我们将两个函数进行复合&#xff0c; 我们不仅要学习拆分函数的&#xff0c;也需要学会聚合函数&#xff0c;例如下面的两个函数&#xff1a;

// 复合下面两个函数&#xff0c;做到先乘3&#xff0c;再开平方
fun square(n: Int) &#61; n * n
fun triple(n: Int) &#61; n * 3

可能第一眼&#xff0c;会这样&#xff1a; val result &#61; square(triple(3))
但这时不是真正的复合函数&#xff0c;只是复合了函数的应用。

下面的答案是以函数编程来进行复合的&#xff1a;

fun compose(f: (Int) -> Int, g: (Int) -> Int): (Int) -> Int &#61; f(g(it))

然后我们就可以通过函数引用的方式&#xff0c;来进行复合:

val squareOfTriple &#61; compse(::square, ::triple)
val result &#61; squareOfTriple(3)

如果想要把 compose 变得更加强大和通用&#xff0c;可以加入泛型&#xff0c; 如下所示&#xff1a;

fun <T, U, V> compose(f: (U) -> V, g(T) -> U): (T) -> V &#61; f(g(it))

这样&#xff0c;我们就把 compose 的功能变得很强大了&#xff0c; 使用泛型&#xff0c;能匹配任何类型的 compose 函数。


2. 高级函数的特征

上面一章学了函数的概念&#xff0c; 但是还没有解答一个最基本的问题&#xff0c;即为什么要将函数作为数据&#xff0c;进行使用或者传递&#xff0c;为什么不只是用 fun 版本&#xff1f; 下面需要来考虑处理多参数的函数。


2.1 多参数函数

没有多个参数的函数&#xff0c; 只有多个参数组成的元组的函数&#xff0c; 就是元组的参数可以是任意多个&#xff0c; 它本身可以是 Pair 或者 Triple 类型等。

现在定义一个函数&#xff0c; 由两个整数相加&#xff0c; 将函数作用于第一个整数&#xff0c;然后返回一个函数&#xff0c; 这个函数的类型是 &#xff1a;

(Int) -> (Int) -> Int

那么这个整数相加的函数就是&#xff1a;

val add: (Int) -> (Int) -> Int &#61; a -> b -> a &#43; b
或者使用 typeAlias 使用类型别名&#xff1a;
typelias IntBinOp &#61; (Int) -> (Int) -> Int
val add: IntBinOp &#61; a -> b -> a &#43; b

那该如果使用 add 函数来将 3 和 5 相加呢&#xff0c;那就要用到上面学习的柯里化函数了&#xff0c; add 函数被认为是等效的元组函数val add: (Int, Int) -> Int &#61; a, b -> a &#43; b 的柯里化形式&#xff0c;使用为&#xff1a;

val result &#61; add(3)(5)

2.2 高阶函数的定义

在上一章中&#xff0c;我们为了达到函数复合 &#xff0c;编写了一个 compose 函数&#xff0c;这种函数接收两个函数组成的元组&#xff0c;作为其参数&#xff0c;并返回一个函数。 但其实可以用值函数来代替 fun 函数&#xff0c; 这种特殊类型的函数&#xff0c; 以函数为参数并返回函数&#xff0c;称之为高阶函数HOF。 下面我们将 compose 函数( Int 版本)&#xff0c; 写成值函数的形式&#xff1a;

先来看看这个 compose 的类型&#xff1a;
因为它原本是 add(f: (Int) -> Int, g(Int) -> Int): (Int) -> Int
所以可以看成是&#xff1a;
((Int) -> Int) -> ((Int) -> Int) -> (Int) -> Int
那么完整代码是&#xff1a;
val compose&#xff1a; ((Int) -> Int) -> ((Int) -> Int) -> (Int) -> Int &#61; x -> y -> z -> x(y(z))
其中 x 是第一个参数函数&#xff0c; y 是第二个参数函数&#xff0c; z 是入参&#xff0c; 函数将 y(z) 的结果应用到 x 函数上
或者使用别名&#xff1a;
typealias IntUnary &#61; (Int) -> Int
val compose: (IntUnary) -> (IntUnary) -> IntUnary &#61; x -> y -> z -> x(y(z))

最后使用&#xff1a;

val square: IntUnary &#61; it * it
val triple: IntUnary &#61; it * 3
// 这里注意下顺序
val squareOfTriple &#61; compose(square)(triple)

2.2.1 compose 的多态高阶版本

上面的 compose 只能符合 Int 到 Int &#xff0c; 我们可以使其多态化&#xff0c;让其复合多种不同的类型&#xff0c;为此我们加入泛型。下面来编写一个多态版本的 compose 值函数&#xff0c;看起来我们只要将上面的 Int 换成泛型就行了&#xff1f;如下

val <T, U, V> higherCompose: ((U) -> V) -> ((T) -> U) -> (T) -> V &#61; f ->
g ->
x ->
f(g(x))



但是这样是不行的&#xff0c;因为 Kotlin 不允许对属性使用泛型&#xff0c; 如果你要使用泛型&#xff0c;只能在类、接口和fun函数上&#xff0c;所以我们只能把其改成 fun 函数&#xff1a;

fun <T, U, V> higherCompose(): .....
也可以写成&#xff1a;
fun <T, U, V> higherCompose() &#61; f: (U) -> V ->
g: (T) -> U ->
x: T ->
f(g(x))



higherCompose 不接收任何函数&#xff0c;并且始终返回相同的值&#xff0c;是一个常函数。接下来使用它时&#xff0c;必须要指明泛型类型&#xff0c;告诉编译器当前函数使用的类型&#xff0c;不然编译会报错&#xff1a;

val squareOfTriple &#61; higherCompose<Int, Int, Int>()(square)(triple)

下面来编写一个 higherCompose &#xff0c; 使得 higherCompose(f)(g) 等价于 higherCompose(g)(f) &#xff0c;很简单&#xff0c;交换下 f 和 g 的类型即可&#xff1a;

fun <T, U, V> higherComposeAndThen() &#61; f: (T) -> U ->
g: (U) -> V ->
x: T ->
g(f(x))





这样的目的是测试参数的顺序&#xff0c; 使用从 Int 到 Int 的函数进行测试将是模棱两可的&#xff0c;因可以按两种顺序符合函数&#xff0c;这样很难检测出错误&#xff0c;我们在测试时&#xff0c;可以使用多种类型


可以看看这样的测试代码&#xff1a;

val f: (Double) -> Int &#61; (it*3).toInt()
val g: (Long) -> Double &#61; it &#43; 2.0
assertEquals(Integer.valueOf(9), f(g(1L)))
assertEquals(Integer.valueOf(9), higherCompose<Long, Double, Int>()(f)(g)(1L))

2.3 使用匿名函数

我们可以使用匿名函数来省略中间函数的定义&#xff0c;例如&#xff1a;

val f: (Double) -> Double &#61; Math.PI / 2 - it
val sin: (Double) -> Double &#61; Math::sin
val cos: Double &#61; compose(f, sin)(2)

可以使用匿名函数写成:

val cosValue: Double &#61; compose( x: Double -> Math.PI / 2 - x , Math::sin)(2.0)

也可以使用高阶函数写成&#xff1a;

val higherCosValue &#61; higherCompose<Double, Double, Double>()( x: Double -> Math.PI / 2 - x )(Math::sin)
可以换成Kotlin官方推荐的Lambda表达式写法&#xff1a;
val higherCosValue &#61; higherCompose<Double, Double, Double>()() x: Double -> Math.PI / 2 - x (Math::sin)

一般来说&#xff0c;匿名函数还命名函数&#xff0c;选择是任意的&#xff0c; 通常下&#xff0c;如果一个函数只使用一次&#xff0c;可以把这个函数弄成匿名函数


2.4 闭包

看下下面代码&#xff1a;

val taxRate &#61; 0.09
fun addTax(price: Double) &#61; price &#43; price * taxRate

上面的 addTaxprice 来说不是一个纯函数&#xff0c;因为函数依赖了参数以外的属性 taxRate&#xff0c; 对于同样的price&#xff0c;它可能会返回不同的结果&#xff08;尽管 taxRate 是使用 val 来声明的&#xff09;。 只能说该函数是元组 (price, taxRate)的纯函数。

所以当函数作为参数传递给其他函数时&#xff0c;它们可能会引发问题。 如果这类函数在同一个类出现很多次&#xff0c;这会使得程序难以阅读或者维护。
为了使得函数易于阅读和维护&#xff0c;一种方法是使他们更加的模块化&#xff0c;这使得程序的每个部分都可以单独的作为一个模块来使用&#xff0c;我们可以通过把元组作为参数来实现&#xff1a;

val taxRate &#61; 0.09
fun addTax(taxRate: Double, price: Double) &#61; price &#43; prie * tax

上面学了多参数处理&#xff0c;所以也可以写成值函数版本或者柯里化版本…

// 值函数 &#43; 闭包
val addTax &#61; taxRate: Double, price: Double -> price &#43; price * taxRate
// 柯里化 &#43; 闭包
val addTax &#61; taxRate: Double ->
price: Double ->
price &#43; price * taxRate



2.5 应用偏函数和自动柯里化

上面写了闭包类型和柯里化类型&#xff0c;虽然对于同样的入参&#xff0c;他们的结果是一样的&#xff0c;但是他们的语义是不一样的
闭包是一股脑的将参数塞入&#xff0c;而柯里化则是层层递进。

上面的柯里化版本&#xff0c;其实就等价于下面的类&#xff1a;

class TaxComputer(private val rate: Double)
fun compute(price: Double): Double &#61; price &#43; price * rate

代码&#xff1a;

val tc9 &#61; TaxComputer(0.09)
val result &#61; tc9.compute(12.0)

等价于柯里化的&#xff1a;

val tc9 &#61; addTax(0.09)
val resulte &#61; tc9(12.0)

可以看到&#xff0c;其实柯里化函数和偏应用函数是密切相关的&#xff0c;可以做到一个参数接一个参数将一个元组给替换为可偏应用的函数。这就是其和元组参数的区别。


2.5.1 例1

试着写一个函数&#xff0c; 双参的柯里化函数&#xff0c;偏应用其第一个参数&#xff0c;推演如下&#xff1a;

假设双参为 A、B&#xff0c;函数返回参数为 C&#xff0c; 那么该函数的类型为&#xff1a;
fun <A, B, C> originFun(): (A) -> (B) -> C
偏应用第一个参数&#xff0c;也就是输入参数A&#xff0c; 得出的结果是一个 (B)-> C 的函数&#xff0c; 可以得到如下的类型&#xff1a;
fun <A, B, C> partialA(a: A, f:(A) -> (B) -> C): (B) -> C
答案很简单&#xff0c;就是将第二个参数应用到第一个参数上&#xff1a;
fun <A, B, C> partialA(a: A, f:(A) -> (B) -> C): (B) -> C &#61; f(a)

2.5.2 例2

试着写一个函数&#xff0c; 也是双参的柯里化函数&#xff0c;偏应用其第二个参数&#xff0c;推演如下&#xff1a;

同样的使用上面的 originFun&#xff1a;
fun <A, B, C> originFun(): (A) -> (B) -> C
第二个参数是 B&#xff0c; 那么要求输入一个B&#xff0c; 得到一个函数为 (A)->C
fun <A, B, C> partialB(b: B, f:(A) -> (B) -> C): (A) -> C
因为 变量是一个 a &#xff0c;所以可以这样开始&#xff1a;
fun <A, B, C> partialB(b: B: f:(A) -> (B) -> C): (A) -> C &#61; a ->
f(a)

f 函数还需要一个b&#xff0c;因为b已经在参数里面了&#xff0c; 所以答案就是&#xff1a;
fun <A, B, C> partialB(b: B: f:(A) -> (B) -> C): (A) -> C &#61; a: A ->
f(a)(b)


2.5.3 例3

写一个函数来将柯里化 (A, B) -> C 类型的函数

已知类型为 fun <A, B, C> origin(a: A, b: B) -> C
那么可以接受这个函数&#xff0c; 并且返回一个柯里化形式的函数&#xff1a;
fun <A, B, C> curry(f: (A, B) -> C): (A) -> (B) -> C &#61; a->
b ->
f(a, b)



2.6 切换偏应用函数的参数

假如一个函数有两个参数&#xff0c;但有时候我们不想直接得到结果&#xff0c;&#xff08;例如其中一个参数还不清楚值&#xff09;&#xff0c;我们只想通过一个参数来获得一个偏应用函数&#xff0c;例如下面的&#xff1a;

val addTax: (Double) -> (Double) -> Double &#61; x->
y ->
y &#43; y/100 * x


开发者可能想先计算税&#xff0c;然后获得一个参数的新函数&#xff0c;然后可以将该函数应用于任何价格&#xff1a;

val add9percentTax: (Double) -> Double &#61; addTax

推荐阅读
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 如何撰写适应变化的高效代码:策略与实践
    编写高质量且适应变化的代码是每位程序员的追求。优质代码的关键在于其可维护性和可扩展性。本文将从面向对象编程的角度出发,探讨实现这一目标的具体策略与实践方法,帮助开发者提升代码效率和灵活性。 ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • Coviam 实习软件工程师的工作体验与成长 ... [详细]
  • V8不仅是一款著名的八缸发动机,广泛应用于道奇Charger、宾利Continental GT和BossHoss摩托车中。自2008年以来,作为Chromium项目的一部分,V8 JavaScript引擎在性能优化和技术创新方面取得了显著进展。该引擎通过先进的编译技术和高效的垃圾回收机制,显著提升了JavaScript的执行效率,为现代Web应用提供了强大的支持。持续的优化和创新使得V8在处理复杂计算和大规模数据时表现更加出色,成为众多开发者和企业的首选。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 如何撰写初级和高级前端开发者的专业简历
    如何撰写初级和高级前端开发者的专业简历 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
  • iOS 设备唯一标识获取的高效解决方案与实践
    在iOS 7中,苹果公司再次禁止了对MAC地址的访问,使得开发者无法直接获取设备的物理地址。为了在开发过程中实现设备的唯一标识,苹果推荐使用Keychain服务来存储和管理唯一的标识符。此外,还可以结合其他技术手段,如UUID和广告标识符(IDFA),以确保设备的唯一性和安全性。这些方法不仅能够满足应用的需求,还能保护用户的隐私。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • PHP自学必备:从零开始的准备工作与工具选择 ... [详细]
  • Kafka 是由 Apache 软件基金会开发的高性能分布式消息系统,支持高吞吐量的发布和订阅功能,主要使用 Scala 和 Java 编写。本文将深入解析 Kafka 的安装与配置过程,为程序员提供详尽的操作指南,涵盖从环境准备到集群搭建的每一个关键步骤。 ... [详细]
author-avatar
湘颖宛光秀雅
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有